#include "NetControllerTCP.h"


CNetControllerTCP::CNetControllerTCP(void)
{
	nMaxClientId = 0;
	nMaxServerId = 0;
}

CNetControllerTCP::~CNetControllerTCP(void)
{
}

// ----------- client specific -----------
int CNetControllerTCP::addClient(string strIp, unsigned short usPort)
{
	SClient* client = new SClient();
	client->id = ++nMaxClientId;
	client->ip = strIp;
	client->port = usPort;
	client->tcp = new TCPManager();

	int size = (int) strIp.size();
	char *acIp = new char[size + 1];
	memcpy(acIp, strIp.c_str(), strIp.size());
	acIp[strIp.size()] = '\0';

	if (client->tcp->Create())
	{
		if (client->tcp->Connect(acIp, usPort))
		{
			// ready
			client->tcp->SetTimeoutSend(0);
			client->tcp->SetTimeoutReceive(0);

			// add client to hash map
			mapClients[client->id] = client;

			delete [] acIp;
			return client->id;
		}
	}

	// error occured -> clean up
	client->tcp->Close();
	delete client->tcp;
	delete client;
	delete [] acIp;
	--nMaxClientId;

	return 0;
}

void CNetControllerTCP::removeClient(int id)
{
	ClientMap::iterator it = mapClients.find(id);

	// check if this is a known id
	if (it != mapClients.end())
	{
		delete ((SClient*) it->second)->tcp;
		delete ((SClient*) it->second);
		mapClients.erase(it);
	}
}

bool CNetControllerTCP::send(unsigned short usType, string strData)
{
	string strMessage = createMessage(usType, strData);

	// iterate through all clients and send message
	ClientMap::iterator it;
	bool bSuccess = true;
	for (it = mapClients.begin(); it != mapClients.end(); it++)
	{
		SClient* client = (SClient*) it->second;
		strMessage += strData;
		int nReturn = client->tcp->SendAll(strMessage.data(), (int) strMessage.size());
		if (nReturn == SOCKET_ERROR)
		{
			//LOG_ERROR((T("socket error sending to client %hs:%u"), (const char*)client->ip.c_str(), client->port));
			bSuccess = false;
		}
	}
	return bSuccess;
}

bool CNetControllerTCP::send(unsigned short usType, string strData, int nId)
{
	// compose send message
	string strMessage = createMessage(usType, strData);

	// find the client and send message
	ClientMap::iterator it = mapClients.find(nId);
	bool bSuccess = true;
	if (it != mapClients.end())
	{
		SClient* client = (SClient*) it->second;
		strMessage += strData;
		int nReturn = client->tcp->SendAll(strMessage.c_str(), (int) strMessage.size());
		if (nReturn == SOCKET_ERROR)
		{
			//LOG_ERROR((T("socket error sending to client %hs:%u"), (const char*)client->ip.c_str(), client->port));
			bSuccess = false;
		}
	}
	else
	{
	    //LOG_ERROR((T("unknown client id %d"), nId));
		bSuccess = false;
	}

	return bSuccess;
}

string CNetControllerTCP::createMessage(unsigned short usType, string strData)
{
	// convert unsigned short to "byte arrays"
	unsigned int unDataSize = unsigned(strData.size());
	string strDataSize = string((char *) &unDataSize, 4);

	string strType = string((char *) &usType, 2);

	return "!" + strDataSize + strType + "!?";
}

// ----------- server specific -----------

int CNetControllerTCP::addServer(unsigned short usPort)
{
	SServer* server = new SServer();
	server->id = ++nMaxServerId;
	server->port = usPort;
	server->tcp = new TCPManager();

	if (server->tcp->Create())
	{
		if (server->tcp->Bind(usPort))
		{
			// ready
			server->tcp->SetTimeoutSend(0);
			server->tcp->SetTimeoutReceive(0);
			server->tcp->SetTimeoutAccept(0);
			server->tcp->Listen();

			// add client to hash map
			mapServers[server->id] = server;

			return server->id;
		}
	}

	// error occured -> clean up
	server->tcp->Close();
	delete server->tcp;
	delete server;
	--nMaxServerId;

	return 0;
}

void CNetControllerTCP::removeServer(int id)
{
	ServerMap::iterator it = mapServers.find(id);

	// check if this is a known id
	if (it != mapServers.end())
	{
		delete ((SServer*) it->second)->tcp;
		delete ((SServer*) it->second);
		mapServers.erase(it);
	}
}

void CNetControllerTCP::accept()
{
	// iterate through all servers accept new connections
	ServerMap::iterator it;
	for (it = mapServers.begin(); it != mapServers.end(); it++)
	{
		SServer* server = (SServer*) it->second;
		// new socket
		TCPManager* tcpNew = new TCPManager();
		tcpNew->Create();
		tcpNew->SetTimeoutReceive(0);
		if (server->tcp->Accept(*tcpNew))
		{
			server->sockets.push_back(tcpNew);
			//LOG_INFO((T("new client connected")));
		}
		else
		{
			tcpNew->Close();
			delete tcpNew;
		}
	}
}
bool CNetControllerTCP::receive(unsigned short* usType, string* strData, int nId)
{
	// find the server and listen
	ServerMap::iterator it = mapServers.find(nId);
	bool bSuccess = true;

	*usType = MSG_NONE;

	if (it != mapServers.end())
	{
		SServer* server = (SServer*) it->second;

		// iterate through actual sockets
		TCPVector::iterator itSockets;
		for (itSockets = server->sockets.begin(); itSockets != server->sockets.end();)
		{
			bool bErased = false;
			char acBuffer[8] = "";
			TCPManager* tcp = (TCPManager*) *itSockets;
			int nReturn = tcp->Receive(acBuffer, 8);
			if (nReturn == SOCKET_ERROR)
			{
				//LOG_ERROR((T("receive error (header) for server on port %u"), server->port));
				// delete this socket
				tcp->Close();
				delete tcp;
				itSockets = server->sockets.erase(itSockets);
				bErased = true;
			}
			else if (acBuffer[0] == '!' && acBuffer[7] == '!')
			{
				// extract message header by casting 4 chars back to unsigned int
				unsigned int unDataSize = *(unsigned int*) &acBuffer[1];
				*usType = *(unsigned short*) &acBuffer[5];

				// check if next part is a valid data package
				nReturn = tcp->Receive(acBuffer, 1);
				if (nReturn == SOCKET_ERROR || acBuffer[0] != '?')
				{
					//LOG_ERROR((T("receive error (data-identifier) for server on port %u"), server->port));
					bSuccess = false;
				}
				else
				{
					// fetch actual message
					char* acData = new char[unDataSize];

					nReturn = tcp->ReceiveAll(acData, unDataSize);
					if (nReturn == SOCKET_ERROR)
					{
						//LOG_ERROR((T("receive error (data) for server on port %u"), server->port));
						bSuccess = false;
					}
					else
					{
						// data without leading '?'
						*strData = string(acData, unDataSize);
					}
					// clean up
					delete [] acData;
				}
			}

			if (!bErased)
			{
				++itSockets;
			}
		}
	}
	else
	{
		//LOG_ERROR((T("unknown server id %d"), nId));
		bSuccess = false;
	}

	return bSuccess;
}
